package org.sword.wechat4j;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.sword.wechat4j.driver.*;
import org.sword.wechat4j.param.SignatureParam;
import org.sword.wechat4j.request.BaseMessage;
import org.sword.wechat4j.response.WechatResponse;
import org.sword.wechat4j.type.MsgType;
import org.sword.wechat4j.util.Config;
import org.sword.wechat4j.util.SerializeXmlUtil;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;

/**
 * 微信框架核心
 */
public class WechatCore {

    public static final Logger logger = Logger.getLogger(WechatCore.class);

    private MessageDriverI messageDriver;
    private EventDriverI eventDriver;
    private Coprocessor coprocessor;

    private String token;

    public WechatCore() {
        this(Config.instance().getToken());
    }

    /**
     * 构建微信处理
     */
    public WechatCore(String token) {
        this.token = token;
    }

    public WechatCore(Coprocessor coprocessor, String token) {
        this.coprocessor = coprocessor;
        this.token = token;
    }

    /**
     * wechat调用入口，进行数据接收，事件分发
     *
     * @param request 微信服务发过来的http请求
     * @return responseXML
     */
    public String run(HttpServletRequest request) {
        SignatureParam param = new SignatureParam(request);
        logger.debug("WechatCore run");
        ValidateSignature validateSignature = new ValidateSignature(param.getSignature(), param.getTimestamp(), param.getNonce(), token);
        if (!validateSignature.check()) {
            logger.debug("check error." + JSON.toJSONString(param) + "\n request:" + JSON.toJSONString(getMessage(request)));
            return "error";
        }
        if (StringUtils.isNotBlank(param.getEchostr())) {

            logger.debug("set :" + JSON.toJSONString(param));
            return param.getEchostr();
        }
        //分发消息，得到响应
        String result = distribute(getMessage(request));
        logger.debug("response data:" + result);
        return result;
    }

    /**
     * 分发处理，得到响应
     *
     * @return responseXML
     */
    private String distribute(BaseMessage message) {

        if (coprocessor != null) {//消息前置处理，如果有。
            logger.debug("message handle before.");
            coprocessor.before(message);
            this.messageDriver = coprocessor.getMessageDriver();
            this.eventDriver = coprocessor.getEventDriver();
        }

        WechatResponse response;
        if (MsgType.event.name().equals(message.getMsgType()) && eventDriver != null) {
            logger.debug("in the event handling...");
            response = eventDriver.service(message);
        } else if (messageDriver != null) {
            logger.debug("in the message handling...");
            response = messageDriver.service(message);
        } else {
            logger.warn("no message handle driver!");
            return "";
        }

        return SerializeXmlUtil.toXML(response);
    }

    /**
     * 添加微信推送消息 处理驱动
     *
     * @param driver 驱动
     */
    public void setDriver(HandleDriver driver) {
        if (MessageDriver.class.isAssignableFrom(driver.getClass())) {
            logger.debug("message driver setting ..." + driver);
            this.messageDriver = (MessageDriver) driver;
        } else if (EventDriver.class.isAssignableFrom(driver.getClass())) {
            logger.debug("event driver setting ..." + driver);
            this.eventDriver = (EventDriver) driver;
        } else {
            logger.warn("unknown handle driver:" + driver);
        }
    }

    /**
     * 从请求体中读取消息
     *
     * @param request servletRequest
     * @return wechat message
     */
    private BaseMessage getMessage(HttpServletRequest request) {
        String xml;
        try {
            xml = readString(request.getReader());
            logger.debug("request data:" + xml);
            return SerializeXmlUtil.toWechatRequest(xml);
        } catch (IOException e) {
            logger.error("post data deal failed!", e);
            return null;
        }
    }

    /**
     * @param reader reader
     * @return string
     * @throws IOException
     */
    private String readString(BufferedReader reader) throws IOException {
        StringBuilder sb = new StringBuilder();
        String tmp;
        while ((tmp = reader.readLine()) != null) {
            sb.append(tmp);
        }
        reader.close();
        return sb.toString();
    }
}
